/// @file PerfLinux.hh
/// @brief Get performance information on Linux

// *************************************************************************

// Copyright© 2010 Texas A&M University, 
//                 College Station, Texas
//                 Contact: R Crosby <rwc@cs.tamu.edu>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// *************************************************************************

#if defined(linux)

#include <stdio.h>
#include <string.h>
#include <linux/limits.h>
#include <sys/resource.h>
#include <unistd.h>

#include "PerfBlock.hh"

static int
LinuxMemoryUsage ( unsigned long & rprvt,    // Resident private 
		   unsigned long & vprvt,    // Virtual private
		   unsigned long & vsize )   // Virtual private + shared
{

    pid_t         pid      = getpid();
    
    char          fname[PATH_MAX];
    int           pvt  = 0;
    FILE *        f;

    rprvt = 0;
    vprvt = 0;
    vsize = 0;

    sprintf(fname, "/proc/%ld/smaps", (long)pid);
    f = fopen(fname, "r");

    if(!f) return -1;

    while(!feof(f)) {

	char buf[PATH_MAX+100];

	if(fgets(buf, sizeof(buf), f) == 0)
	    break;

	unsigned long res;

	if (sscanf ( buf, 
		     "Rss: %ld kB",
		     &res) ) {
		     
	    if (pvt) rprvt += res;

	}
	else {

	    char          perm[5];
	    char          dev[6];
	    char          mapname[PATH_MAX];
	    unsigned long begin;
	    unsigned long end;
	    unsigned long inode;
	    unsigned long foo;

	    mapname[0] = 0;

	    if (sscanf ( buf, 
		     "%lx-%lx %4s %lx %5s %ld %s", 
			 &begin, &end, perm, &foo, dev, &inode, mapname) ) {

		    unsigned long size = end - begin;
		    vsize += size;

		    /* the permission string looks like "rwxp", where each character can
		     * be either the letter, or a hyphen.  The final character is either
		     * p for pvt or s for shared.  We want to add up private writable
		     * mappings, to get a feel for how much private memory this process
		     * is taking.
		     *
		     * Also, we add up the shared mappings, to see how much this process
		     * is sharing with others.
		     */

		    if(perm[3] == 'p') {
			pvt = 1;
			if(perm[1] == 'w')    
			    vprvt += size;
		    } else {
			pvt = 0;
		    }
		}

	}

    }

    fclose(f);

    rprvt *= 1024;				  // Make bytes instead of kb

    return 0;

}

int
GetPerformanceInfo ( PerfBlock * pb )
{

    int rc = LinuxMemoryUsage ( pb->resPrivate,
			        pb->virPrivate,
			        pb->virSize );
    if (rc) return rc;

    rusage udata;
    rc = getrusage( RUSAGE_SELF, &udata );
    if (rc) return rc;

    pb->userTime = udata.ru_utime;
    pb->sysTime  = udata.ru_stime;

    pb->minorFaults  = udata.ru_minflt;
    pb->majorFaults  = udata.ru_majflt;
    pb->volContextSw = udata.ru_nvcsw;
    pb->involContextSw = udata.ru_nivcsw;

    return 0;

}

#endif
